/***********************************************************************************
Copy right:	    hqyj Tech.
Author:         jiaoyue
Date:           2023.07.01
Description:    http请求处理
***********************************************************************************/

#include "custom_handle.h"

#define KB 1024
#define HTML_SIZE (64 * KB)

// 普通的文本回复需要增加html头部
#define HTML_HEAD "Content-Type: text/html\r\n" \
                  "Connection: close\r\n"

static struct shm_param para1, para2; // 共享内存参数
// volatile MQTTClient_deliveryToken deliveredtoken;
static int *total_stm;          // 指向共享内存中数据节点总个数
static int *total_mod;          // 指向共享内存中Modbus节点总个数
static stm_node_p node_arr;     // 指向共享内存中节点缓存数组头
static modbus_set_p modbus_arr; // 指向共享内存中Modbus节点缓存数组头
static uint16_t flush_flag = 0; // 刷新标志
static uint16_t send_type = 2;  // 发送类型
static msgbuf_t send_buf;       // 消息缓冲区
static int time_period = 2;     // 上报周期

// 获取共享内存数据
char *shm_get() // 获取共享内存数据
{
    int ret_stm = -1, ret_mod = -1;
    ret_stm = shm_init(&para1, "shm_stm32", MAX_NODE_SIZE); // 初始化stm共享内存
    ret_mod = shm_init(&para2, "shm_mod", MAX_NODE_SIZE);   // 初始化mod共享内存
    if (ret_stm < 0 || ret_mod < 0)
    {
        printf("Failed to initialize shared memory\n");
        return NULL;
    }
    char switch_buf[128];
    void *node_p_stm = shm_getaddr(&para1); // 获取stm共享内存地址
    void *node_p_mod = shm_getaddr(&para2); // 获取mod共享内存地址
    if (node_p_stm == NULL || node_p_mod == NULL)
    {
        printf("Failed to get shared memory address\n");
        return NULL;
    }

    total_stm = (int *)node_p_stm; // 前4个字节存储实际的数据节点总个数
    total_mod = (int *)node_p_mod;
    node_arr = (stm_node_p)(node_p_stm + sizeof(int));     // 后面空间存储stm数据点
    modbus_arr = (modbus_set_p)(node_p_mod + sizeof(int)); // 后面空间存储Modbus数据点
    printf("total_stm: %d, total_mod: %d\n", *total_stm, *total_mod);
    cJSON *root = cJSON_CreateObject(); // 创建根节点
    cJSON *data = cJSON_CreateArray();
    cJSON *num = cJSON_CreateNumber((*total_stm) + (*total_mod));
    cJSON_AddItemToObject(root, "total", num); // 添加总数

    for (int i = 0; i < (*total_stm); i++) // 遍历所有数据节点
    {
        cJSON *member = cJSON_CreateObject();
        cJSON *key = cJSON_CreateNumber(node_arr[i].key); // 读取dev_type值
        cJSON_AddItemToObject(member, "key", key);

        if (node_arr[i].type == TYPE_BOOL)
            snprintf(switch_buf, sizeof(switch_buf), "%s", node_arr[i].new_val.b_val ? "TRUE" : "FALSE");
        else if (node_arr[i].type == TYPE_FLOAT)
            snprintf(switch_buf, sizeof(switch_buf), "%.2f", node_arr[i].new_val.f_val);
        else if (node_arr[i].type == TYPE_INT)
            snprintf(switch_buf, sizeof(switch_buf), "%d", node_arr[i].new_val.i_val);

        cJSON *value = cJSON_CreateString(switch_buf);
        cJSON_AddItemToObject(member, "value", value);
        cJSON_AddItemToArray(data, member);
        memset(switch_buf, 0, sizeof(switch_buf));
    }
    for (int i = 0; i < (*total_mod); i++) // 遍历所有modbus数据节点
    {
        cJSON *member = cJSON_CreateObject();
        cJSON *key = cJSON_CreateNumber(modbus_arr[i].key); // 读取key值
        cJSON_AddItemToObject(member, "key", key);
        if (modbus_arr[i].type == TYPE_BOOL)
            snprintf(switch_buf, sizeof(switch_buf), "%s", modbus_arr[i].new_val.b_val ? "TRUE" : "FALSE");
        else if (modbus_arr[i].type == TYPE_FLOAT)
            snprintf(switch_buf, sizeof(switch_buf), "%.2f", modbus_arr[i].new_val.f_val);
        else if (modbus_arr[i].type == TYPE_INT)
            snprintf(switch_buf, sizeof(switch_buf), "%d", modbus_arr[i].new_val.i_val);
        cJSON *value = cJSON_CreateString(switch_buf); // 读取value值
        cJSON_AddItemToObject(member, "value", value);
        cJSON_AddItemToArray(data, member);
        memset(switch_buf, 0, sizeof(switch_buf));
    }
    cJSON_AddItemToObject(root, "data", data);
    char *p = cJSON_Print(root); // 获取JSON字符串
    return p;
}

// 读取数据库，对比登录信息
int db_login(char *uname, char *passwd)
{
    int ret = -1;
    sqlite3 *db;
    // 打开或创建数据库
    if (sqlite3_open(user_db_path, &db) != SQLITE_OK)
    {
        fprintf(stderr, "open err:%s\n", sqlite3_errmsg(db));
        return -1;
    }
    // 查询对比
    char **result = NULL;
    int row, column;
    char *errmsg = NULL;
    sqlite3_get_table(db, "select * from user_manage ", &result, &row, &column, &errmsg);
    // printf("row:%d column:%d\n", row, column);
    // int k = 0;
    // printf("aaaa:%s ", result[k++]);
    for (int i = 1; i <= row; ++i)
    {
        const char *db_user = result[i * column + 0];
        const char *db_pass = result[i * column + 1];
        if (strcmp(db_user, uname) == 0 && strcmp(db_pass, passwd) == 0)
        {
            sqlite3_free_table(result);
            sqlite3_close(db);
            return 1;
        }
    }
    sqlite3_free_table(result);
    // 关闭数据库
    sqlite3_close(db);
    return 0;
}
// 拿取点表数据
cJSON *load_json_file(const char *filename)
{
    // 1. 打开JSON文件（只读模式）
    FILE *file = fopen(filename, "r");
    if (!file)
    { // 打开失败处理
        fprintf(stderr, "无法打开JSON文件: %s\n", filename);
        return NULL;
    }

    // 2. 获取文件长度（用于分配缓冲区）
    fseek(file, 0, SEEK_END);  // 将文件指针移到末尾
    long length = ftell(file); // 获取当前指针位置（即文件长度）
    fseek(file, 0, SEEK_SET);  // 指针移回开头

    // 3. 分配缓冲区存储文件内容（+1是为了给字符串结尾的'\0'留空间）
    char *buffer = malloc(length + 1);
    if (!buffer)
    {                 // 内存分配失败处理
        fclose(file); // 先关闭文件再返回
        return NULL;
    }

    // 4. 读取文件内容到缓冲区
    fread(buffer, 1, length, file); // 读取length字节到buffer
    fclose(file);                   // 读取完成后关闭文件
    buffer[length] = '\0';          // 手动添加字符串结束符

    // 5. 解析缓冲区中的JSON字符串为cJSON对象
    cJSON *json_data = cJSON_Parse(buffer);
    free(buffer); // 解析完成后释放缓冲区（不再需要原始字符串）

    return json_data; // 返回解析后的JSON对象
}
// 登录页面处理
static int handle_login(int sock, const char *input)
{
    char reply_buf[HTML_SIZE] = {0};
    char *uname = strstr(input, "username=");
    uname += strlen("username=");
    char *p = strstr(input, "password");
    *(p - 1) = '\0';
    printf("username = %s\n", uname);

    char *passwd = p + strlen("password=");
    printf("passwd = %s\n", passwd);
    // 对比数据库
    int info = db_login(uname, passwd);

    // 登录验证
    if (info == 1)
    {
        strcpy(reply_buf, "<script>window.location.href = '/index.html';</script>");
        // fputs(reply_buf, stdout);
        send(sock, reply_buf, strlen(reply_buf), 0);
    }
    else
    {
        // strcpy(reply_buf, "<script charset='UTF-8'>alert('用户名或密码错误');</script>");
        // html页面要解析数据，+charset='UTF-8'会影响数据解析
        strcpy(reply_buf, "<script>alert('用户名或密码错误');</script>");
        strcat(reply_buf, "<script>window.location.href = '/login.html';</script>");
        // fputs(reply_buf, stdout);
        send(sock, reply_buf, strlen(reply_buf), 0);
    }
    return 0;
}

// 求和页面处理
static int handle_add(int sock, const char *input)
{
    int number1, number2;

    // input必须是"data1=1data2=6"类似的格式，注意前端过来的字符串会有双引号
    sscanf(input, "\"data1=%ddata2=%d\"", &number1, &number2);
    printf("num1 = %d\n", number1);

    char reply_buf[HTML_SIZE] = {0};
    printf("num = %d\n", number1 + number2);
    sprintf(reply_buf, "%d", number1 + number2);
    printf("resp = %s\n", reply_buf);
    send(sock, reply_buf, strlen(reply_buf), 0);
    return 0;
}

/**
 * @brief 处理自定义请求，在这里添加进程通信
 * @param input
 * @return
 */
// 解析并处理自定义请求
int parse_and_process(int sock, const char *query_string, const char *input)
{
    // query_string不一定能用的到

    // 先处理登录操作
    if (strstr(input, "username=") && strstr(input, "password="))
    {
        return handle_login(sock, input);
    }
    // 处理求和请求
    else if (strstr(input, "data1=") && strstr(input, "data2="))
    {
        return handle_add(sock, input);
    }
    else // 剩下的都是json请求，这个和协议有关了
    {
        // 解析JSON字符串
        cJSON *root = cJSON_Parse(input);
        if (root == NULL)
        {
            perror("err: cJSON_Parse");
            return -1;
        }
        // 解析type字段
        // 第一个字段type表示要执行的操作，1：采集/采集回复 2：控制/控制回复 3.模式修改 4.历史数据查询
        cJSON *command = cJSON_GetObjectItem(root, "type");
        // printf("type = %d\n", command->valueint);
        // 采集指令
        if (command->valueint == 1)
        {
#if 0
            //以下数据暂时没有使用到
            cJSON *limit = cJSON_GetObjectItem(root, "limit");
            printf("limit = %s\n", limit->valuestring);
            cJSON *key = cJSON_GetObjectItem(root, "key");
            int count = cJSON_GetArraySize(key);
            cJSON *tmp;
            for (int i = 0; i < count; i++)
            {
                tmp = cJSON_GetArrayItem(key, i);
                key = cJSON_GetObjectItem(tmp, "key");
                printf("key = %d\n", key->valueint);
            }
#endif
#if 1
            // 采集指令
            char *p = shm_get();
            cJSON *root = cJSON_Parse(p);
            if (root == NULL)
            {
                perror("root cJSON_Parse");
                return -1;
            }
            // 添加 type 字段(采集/采集回复)
            cJSON_AddItemToObject(root, "type", cJSON_CreateNumber(1));
            // 转换为字符串
            char *response = cJSON_Print(root);
            printf("response:\n%s\n", response);
            send(sock, response, strlen(response), 0);
            // 释放
            free(p);
            free(response);
            cJSON_Delete(root);
#else
            // 调试数据
            const char *json_data =
                "{\n"
                "  \"total\": 2,\n"
                "  \"type\": 1,\n"
                "  \"data\": [{\n"
                "    \"key\": 302,\n"
                "    \"value\": \"60\"\n"
                "  },{\n"
                "    \"key\": 301,\n"
                "    \"value\": \"28\"\n"
                "  }]\n"
                "}";
            send(sock, json_data, strlen(json_data), 0);
#endif
        }
        // 控制
        else if (command->valueint == 2)
        {
            // 控制指令
            // 解析json格式的数据请求
            cJSON *key = cJSON_GetObjectItem(root, "key");
            cJSON *value = cJSON_GetObjectItem(root, "val");
            cJSON *equip = cJSON_GetObjectItem(root, "equip");
            // 将数据写入消息队列结构体
            struct msgbuf send_buf;
            send_buf.mtype = 2;
            send_buf.mkey = key->valueint;
            strcpy(send_buf.value, value->valuestring);
            switch (key->valueint)
            {
            case 101:
                send_buf.addr = 10000;
                break;
            case 102:
                send_buf.addr = 10001;
                break;
            case 103:
                send_buf.addr = 10002;
                break;
            case 104:
                send_buf.addr = 10003;
                break;
            default:
                // send_buf.addr = 0; // 没有地址，发送给单片机的数据，应该用不到，先赋0
                break;
            }
#if 0
            printf("-------------结构体数据---------------\n");
            printf("mtype = %d\n", send_buf.mtype);
            printf("mkey = %d\n", send_buf.mkey);
            printf("value = %s\n", send_buf.value);
            printf("addr = %d\n", send_buf.addr);
#endif

            if (equip->valueint == 1) // 发送给单片机
            {
                if (msg_queue_send("msg_stm", &send_buf, sizeof(send_buf), 0) < 0)
                {
                    printf("msg_stm error");
                }
            }
            else
            {
                if (msg_queue_send("msg_mod", &send_buf, sizeof(send_buf), 0) < 0)
                {
                    printf("msg_mod error");
                }
            }
            send(sock, "ok", strlen("ok"), 0);
        }
        else if (command->valueint == 3)
        {
#if 0

            const char *json_data =
                "{\n"
                "  \"type\": 3,\n"
                "  \"http\": {\n"
                "    \"host_name\": \"linux\",\n"
                "    \"firm_ver\": \"v1.0\",\n"
                "    \"ip\": \"192.168.51.74\"\n"
                "  }\n"
                "}";
            // 发送采集指令
            send(sock, json_data, strlen(json_data), 0);
#else
            // 1. 加载并解析config.json文件
            cJSON *json_data = load_json_file(project_path);
            if (json_data == NULL)
            { // 加载失败处理
                const char *error_msg = "{\"type\":3, \"error\":\"无法加载JSON文件\"}";
                send(sock, error_msg, strlen(error_msg), 0); // 发送错误响应
                return 0;
            }

            // 2. 从JSON中提取"http"节点（区分大小写）
            cJSON *http_section = cJSON_GetObjectItem(json_data, "http");
            if (http_section == NULL)
            {                            // 找不到http节点处理
                cJSON_Delete(json_data); // 先释放已解析的JSON对象
                const char *error_msg = "{\"type\":3, \"error\":\"JSON文件中找不到http部分\"}";
                send(sock, error_msg, strlen(error_msg), 0); // 发送错误响应
                return 0;
            }

            // 3. 构建响应JSON
            cJSON *response = cJSON_CreateObject();                         // 创建空JSON对象
            cJSON_AddItemToObject(response, "type", cJSON_CreateNumber(3)); // 添加type字段（值为3）

            // 复制http节点的内容到响应的data字段（1表示深拷贝，递归复制所有子节点）
            cJSON *data = cJSON_Duplicate(http_section, 1);
            cJSON_AddItemToObject(response, "http", data); // 添加data字段（值为http节点内容）

            // 4. 将响应JSON转换为字符串并发送
            char *response_str = cJSON_PrintUnformatted(response); // 转换为无格式的JSON字符串
            send(sock, response_str, strlen(response_str), 0);     // 通过网络发送（sock是已建立的套接字）

            // 5. 释放所有资源（避免内存泄漏）
            free(response_str);      // 释放JSON字符串
            cJSON_Delete(response);  // 释放响应JSON对象
            cJSON_Delete(json_data); // 释放原始JSON对象

#endif
        }
        else
        {
            printf("command error");
        }
    }
    return 0;
}
